<template>
    <Row class="condition-edior" type="flex">
        <Col :span="5" class="list-box">
            <SelectList :select-active="false" @select="addPartParam" :hasFilter="true" :data="paramFileds" :select="false" title="参数变量">
              <template slot="option" slot-scope="data">{{data.item.name}} {{data.item.code}}</template>
            </SelectList>
        </Col>
        <Col :span="19" class="content-box">
            <div class="tool-box">
                <div class="operate-box">
                   <div class="operate-list">
                        <div class="operate-item" @click="modeTransform" v-if="!sourceMode"><Icon type="code"></Icon> 高级模式</div>
                        <div class="operate-item" @click="modeTransform" v-else><Icon class="active" type="code-working"></Icon> 基础模式</div>
                        <div class="operate-item" @click="evalFormOpen"><Icon type="erlenmeyer-flask" :class="{active:evalTrigger}"></Icon> 条件试算</div>
                        <div class="operate-item" disbaled>插入函数 <Icon type="arrow-down-b"></Icon></div>
                    </div>
                    <div class="operate-list">
                        <div class="operate-item"  @click="addPartLogic('==')">等于</div>
                        <div class="operate-item"  @click="addPartLogic('!=')">不等于</div>
                        <div class="operate-item" @click="addPartLogic('&lt;')">小于</div>
                        <div class="operate-item" @click="addPartLogic('&lt;=')">小于等于</div>
                        <div class="operate-item" @click="addPartLogic('&gt;')">大于</div>
                        <div class="operate-item" @click="addPartLogic('&gt;=')">大于等于</div>
                        <div class="operate-item" @click="addPartLogic('&&')">并且</div>
                        <div class="operate-item" @click="addPartLogic('||')">或者</div>
                        <div class="operate-item" @click="addPartLogic('!')">非</div>
                    </div>
                    <div class="operate-list">
                        <div class="operate-item" @click="addPartMath('+')">+</div>
                        <div class="operate-item" @click="addPartMath('-')">-</div>
                        <div class="operate-item" @click="addPartMath('*')">x</div>
                        <div class="operate-item" @click="addPartMath('/')">/</div>
                        <div class="operate-item" @click="addPartMath('(')">(</div>
                        <div class="operate-item" @click="addPartMath(')')">)</div>
                        <div class="operate-item" @click="backspace()"><Icon type="backspace"></Icon></div>
                    </div>
                </div>
                <div class="number-box">
                    <table class="number-table">
                        <tr>
                            <td @click="add('7')">7</td>
                            <td @click="add('8')">8</td>
                            <td @click="add('9')">9</td>
                        </tr>
                        <tr>
                            <td @click="add('4')">4</td>
                            <td @click="add('5')">5</td>
                            <td @click="add('6')">6</td>
                        </tr>
                        <tr>
                            <td @click="add('1')">1</td>
                            <td @click="add('2')">2</td>
                            <td @click="add('3')">3</td>
                        </tr>
                        <tr>
                            <td colspan="2" @click="add('0')">0</td>
                            <td @click="add('.')">.</td>
                        </tr>
                    </table>
                </div>
            </div>
            <Modal v-model="evalOpen" class="modal-full" title="试算参数配置" width=600 @on-ok="setEvalData">
              <FormControl ref="form" :model="{fields:paramFileds}"  :col="1" :hasFullRow="false" :validate="false"></FormControl>
            </Modal>
            <div class="code-box" ref="codeBox">
                <div class="code-editor" ref="code" v-if="!sourceMode" :contenteditable="sampleEditable" @keydown="codeKeyDown" @input="codeInput" v-html="showCode"></div>
                <textarea class="code-editor" v-else v-model="sourceCode"></textarea>
                <div class="status-box">
                  <span v-show="evalTrigger && evalResult!==null" :class="{orange:!evalResult,green:evalResult}">试算结果：<span>{{evalResult}}</span></span>
                  <span v-if="evalResult!==null"><Icon type="checkmark-circled"></Icon> 格式合法</span>
                  <span v-else class="red"><Icon type="close-circled"></Icon> 格式错误</span>
                </div>
            </div>
        </Col>
    </Row>
</template>

<script>
/*
字符串识别策略
需 表达式 提取非变量表示式（字母或下划线开头，包括字母，下划线和减号）
*/
import SelectList from "@/components/selectPanel";
import $ from "@/common";
import Exp from "./exp";
import MathEval from "@/common/formula";
Exp;
const expReg = {
  param: new RegExp("\\[\\'(.| )+?\\'\\]", "igm"),
  paramCode: new RegExp(/[\[\',\'\]]/gim)
};
export default {
  props: {
    params: {},
    data: {
      default: ""
    }
  },
  data() {
    return {
      //简单模式下开放编辑
      sampleEditable: false,
      sourceMode: false,
      sourceCode: "",
      showCode: "",
      //参数字段
      paramFileds: [],
      fieldMap: {},
      //试算字段数据
      evalData: [],
      evalOpen: false,
      evalTrigger: false,
      formulaError: null
    };
  },
  computed: {
    //试算结果
    evalResult() {
      var v = null;
      if (!this.sourceMode) {
        this.syncSoureCode();
      }
      try {
        v = MathEval(this.trimParam(this.sourceCode), this.evalData, true);
      } catch (ex) {
        console.warn(ex);
      }
      return v;
    }
  },
  watch: {
    sourceMode(v) {
      if (v) {
        this.syncSoureCode();
      } else {
        this.syncShowCode();
      }
    },
    data(v) {
      this.sourceCode = v;
      if (this.sourceCode && this.sourceCode.length) {
        this.syncShowCode();
      }
    },
    sourceCode(v) {
      this.$emit("input", v);
    }
  },
  methods: {
    syncSoureCode() {
      var code = this.showCode;
      //清理字符串中的大于小于号，避免扰乱标签正则匹配
      code = code.replace($.regExp.string, function(str) {
        return str.replace(/</gim, "&lt;").replace(/>/gim, "&gt;");
      });
      this.sourceCode = code.replace(/<input (.*?) >/gim, str => {
        //恢复转移字符
        str = str.replace(/&lt;/gim, "<").replace(/&gt;/gim, ">");
        var m = str
          .replace(/&lt;/gim, "<")
          .replace(/&gt;/gim, ">")
          .match(/value="(.*?)"/);
        if (m && m[1]) {
          //通过data属性判断标签是否是参数变量
          var d = str.match(/data="(.*?)"/);
          if (d && d[1]) {
            return this.paramCode(d[1]);
          } else {
            return m[1];
          }
        } else {
          return "";
        }
      });
    },
    syncShowCode() {
      var code = this.sourceCode;
      code = code
        .replace(
          /(\>\=|\<\=|\|\||\=\=|\!\=|\&\&|[\<\>\+\-\!\*/\(\)])/gim,
          (str, value) => {
            //操作符替换
            return `<input class="part" disabled="disabled" value="${value}" style="width:${$.getFontWidth(
              value,
              this.$refs.codeBox
            )}" >`;
          }
        )
        .replace(expReg.param, str => {
          //参数变量替换
          code = this.getParamCode(str);
          var name = this.fieldMap[code] ? this.fieldMap[code].name : code;
          return `<input class="part param" data="${code}" disabled="disabled" value="${name}" style="width:${$.getFontWidth(
            name,
            this.$refs.codeBox
          )}" >`;
        });
      this.showCode = code;
    },
    addPartLogic(char) {
      this.addPart(char, "logic");
    },
    addPartMath(char) {
      this.addPart(char, "math");
    },
    addPartParam(index) {
      var data = this.paramFileds[index];
      this.addPart(data.name, "param", data.code);
    },
    trimParam(str) {
      if (str && typeof str === "string") {
        return str.replace(expReg.param, s => {
          return s.replace(expReg.paramCode, "").replace(/^\s+|\s+$/gm, "");
        });
      } else {
        return str;
      }
    },
    getParamCode(str) {
      if (str && typeof str === "string") {
        return str.replace(expReg.paramCode, "").replace(/^\s+|\s+$/gm, "");
      }
    },
    codeInput(e) {
      if (e.data && !e.data.match(/[\w.]/)) {
        e.returnValue = false;
        e.data = false;
        return false;
      }
    },
    addPart(char, className, data) {
      if (!this.sourceMode) {
        this.showCode += `<input class="part ${className}" data="${data ||
          ""}" disabled="disabled" value="${char}" style="width:${$.getFontWidth(
          char,
          this.$refs.codeBox
        )}" >`;
      } else {
        this.sourceCode += data ? this.paramCode(data) : char;
      }
    },
    paramCode(code) {
      return "['" + code + "']";
    },
    codeKeyDown(e) {},
    add(char) {
      if (!this.sourceMode) {
        this.showCode += char;
      } else {
        this.sourceCode += char;
      }
    },
    modeTransform() {
      this.sourceMode = !this.sourceMode;
    },
    backspace() {
      var code = this.showCode;
      if (!code) return;
      if (code[code.length - 1] === ">") {
        var m = code.match(/<input (.*?) >/gim);
        if (m && m.length && $.lastMatch(code, m[m.length - 1])) {
          code = code.slice(0, code.length - m[m.length - 1].length);
        } else {
          code = code.replace(/.$/gim, "");
        }
      } else {
        code = code.replace(/.$/gim, "");
      }
      this.showCode = code;
    },
    fetchParamFields() {
      this.$set(this, "paramFileds", this.params || []);
      for (let field of this.paramFileds) {
        this.fieldMap[field.code] = field;
      }
    },
    evalFormOpen() {
      this.evalOpen = true;
    },
    setEvalData() {
      this.evalTrigger = true;
      this.$set(this, "evalData", $.copy(this.$refs.form.formData));
    }
  },
  mounted() {
    this.sourceCode = this.data;
    if (this.sourceCode && this.sourceCode.length) {
      this.syncShowCode();
    }
  },
  beforeCreate() {
    this.$options.components.FormControl = require("@/components/form");
  },
  created() {
    this.fetchParamFields();
  },
  components: {
    SelectList
  }
};
</script>

<style lang="less">
.condition-edior {
  height: auto;
  .content-box {
    height: 100%;
    padding-left: 16px;
    display: flex;
    flex-direction: column;
    .code-box {
      flex: 1;
    }
    .tool-box {
      display: flex;
    }
  }
  .list-box {
    height: 100%;
    /*当前组件针对列表组件的临时设置*/
    > .select-list {
      height: 100%;
      display: flex;
      flex-direction: column;
      .search-line {
        .search-input {
          input {
            height: 26px;
          }
        }
        button.ivu-btn {
          height: 26px;
          padding-right: 8px;
          padding-left: 6px;
          padding: 4px 8px;
        }
      }
      .ivu-card-head {
        font-size: 12px;
        padding: 10px 8px 6px;
      }
      .select-list-body {
        flex: 1;
        display: flex;
        flex-direction: column;
        .list-item {
          padding: 6px 8px;
        }
        .list-wrapper {
          overflow: auto;
        }
      }
    }
  }
  .ivu-icon.active {
    color: #62b6ff;
  }
}

.operate-box {
  float: left;
  margin-top: -2px;
  margin-right: 8px;
  flex: 1;
  .operate-list {
    clear: both;
    padding: 4px 0;
    display: flex;
    .operate-item {
      flex: 1;
      min-width: 24px;
      display: block;
      text-align: center;
      padding: 4px 0;
      white-space: nowrap;
      cursor: pointer;
      color: #163992;
      border: 1px solid #dddee1;
      background: #f7f7f7;
      border-radius: 0;
      border-left-width: 0;
      float: left;
      user-select: none;
      &:hover {
        background: #f2f4ff;
      }
      &:active {
        background: #edf1fc;
      }
      &[disbaled] {
        color: #b7bcc8;
        &:hover,
        &:active {
          cursor: default;
          background: #f7f7f7;
        }
      }
      &:first-child {
        border-left-width: 1px;
        border-radius: 4px 0 0 4px;
      }
      &:last-child {
        border-radius: 0 4px 4px 0;
        &::after {
          content: "\0020";
          display: block;
          height: 0;
          clear: both;
        }
      }
    }
  }
}

.code-box {
  height: 100%;
  overflow: auto;
  background: #fff;
  border-radius: 4px;
  border: 1px solid #dddee1;
  width: 100%;
  padding: 8px;
  font-size: 16px;
  font-weight: bold;
  outline: none;
  display: flex;
  flex-direction: column;
  .part {
    color: #08f;
    user-select: none;
    display: inline;
    background: transparent;
    border: none;
    padding: 0;
    white-space: nowrap;
    margin: 0 4px;
    text-align: center;
    &.logic {
    }
    &.param {
      color: #f380af;
      font-size: 16px;
    }
    &.math {
      color: #7ec3ff;
    }
  }
  .status-box {
    font-size: 12px;
    font-weight: normal;
    border-top: 1px solid #dddee1;
    margin: 0px -8px -8px;
    padding: 8px 8px;
    text-align: right;
    color: #168cf3;
    background: #f7f7f7;
    > span {
      margin: 0 8px;
    }
  }

  .code-editor {
    outline: none;
    height: 100%;
    outline: none;
    height: 100%;
    width: 100%;
    background: none;
    border: none;
    resize: none;
    font-size: 16px;
  }
  textarea.code-editor {
    font-weight: normal;
    font-size: 16px;
  }
}
.number-box {
  float: right;
  .number-table {
    margin-bottom: 8px;
    td {
      width: 40px;
      height: 24px;
      text-align: center;
      vertical-align: middle;
      border-radius: 4px;
      cursor: pointer;
      color: #168cf3;
      font-size: 14px;
      user-select: none;
      /* font-weight: bold; */
      border: 1px solid #168cf3;
      &:hover {
        border-color: #43a8ff;
        color: #43a8ff;
      }
      &:active {
        background-color: #168cf3;
        color: #fff;
      }
    }
  }
}
</style>
